package com.ltcode.auth.infrastructure.repository.impl;

import cn.hutool.core.collection.ListUtil;
import com.ltcode.auth.domain.model.AuthToken;
import com.ltcode.auth.domain.repository.AuthTokenRepository;
import com.ltcode.cache.service.RedisService;
import com.ltcode.common.base.constant.CacheConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 认证令牌仓储实现
 * 
 * @author ltcode
 * @since 2024-01-01
 */
@Slf4j
@Repository
@RequiredArgsConstructor
public class AuthTokenRepositoryImpl implements AuthTokenRepository {

    private final RedisService redisService;

    private static final String TOKEN_KEY_PREFIX = CacheConstants.LOGIN_TOKEN_KEY;
    private static final String USER_TOKEN_KEY_PREFIX = "auth:user:tokens:";
    private static final long DEFAULT_EXPIRE_MINUTES = CacheConstants.EXPIRATION;

    @Override
    public AuthToken save(AuthToken token) {
        try {
            String tokenKey = getTokenKey(token.getTokenId());
            String userTokenKey = getUserTokenKey(token.getUserId());
            
            // 保存令牌信息
            redisService.setCacheObject(tokenKey, token, DEFAULT_EXPIRE_MINUTES, TimeUnit.MINUTES);
            
            // 将令牌ID添加到用户令牌集合中
            redisService.setCacheSet(userTokenKey, new HashSet<>(Collections.singleton(token.getTokenId())));
            redisService.expire(userTokenKey, DEFAULT_EXPIRE_MINUTES, TimeUnit.MINUTES);
            
            log.debug("保存令牌成功: tokenId={}, userId={}", token.getTokenId(), token.getUserId());
            return token;
        } catch (Exception e) {
            log.error("保存令牌失败: tokenId={}", token.getTokenId(), e);
            throw new RuntimeException("保存令牌失败", e);
        }
    }

    @Override
    public Optional<AuthToken> findByTokenId(String tokenId) {
        try {
            String tokenKey = getTokenKey(tokenId);
            AuthToken token = redisService.getCacheObject(tokenKey);
            return Optional.ofNullable(token);
        } catch (Exception e) {
            log.error("查询令牌失败: tokenId={}", tokenId, e);
            return Optional.empty();
        }
    }

    @Override
    public List<AuthToken> findActiveTokensByUserId(Long userId) {
        try {
            String userTokenKey = getUserTokenKey(userId);
            Set<String> tokenIds = redisService.getCacheSet(userTokenKey);
            
            return tokenIds.stream()
                    .map(this::findByTokenId)
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .filter(token -> AuthToken.TokenStatus.ACTIVE.equals(token.getStatus()))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("查询用户活跃令牌失败: userId={}", userId, e);
            return ListUtil.empty();
        }
    }

    @Override
    public List<AuthToken> findByUserIdAndType(Long userId, AuthToken.TokenType tokenType) {
        try {
            return findActiveTokensByUserId(userId).stream()
                    .filter(token -> tokenType.equals(token.getTokenType()))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("根据用户ID和类型查询令牌失败: userId={}, tokenType={}", userId, tokenType, e);
            return ListUtil.empty();
        }
    }

    @Override
    public void deleteByTokenId(String tokenId) {
        try {
            Optional<AuthToken> tokenOpt = findByTokenId(tokenId);
            if (tokenOpt.isPresent()) {
                AuthToken token = tokenOpt.get();
                String tokenKey = getTokenKey(tokenId);
                String userTokenKey = getUserTokenKey(token.getUserId());
                
                // 删除令牌
                redisService.deleteObject(tokenKey);
                
                // 从用户令牌集合中移除
//                redisService.delCacheSetObject(userTokenKey, tokenId);
                
                log.debug("删除令牌成功: tokenId={}", tokenId);
            }
        } catch (Exception e) {
            log.error("删除令牌失败: tokenId={}", tokenId, e);
        }
    }

    @Override
    public void revokeAllTokensByUserId(Long userId) {
        try {
            List<AuthToken> tokens = findActiveTokensByUserId(userId);
            for (AuthToken token : tokens) {
                token.revoke();
                save(token);
            }
            log.debug("撤销用户所有令牌成功: userId={}", userId);
        } catch (Exception e) {
            log.error("撤销用户所有令牌失败: userId={}", userId, e);
        }
    }

    @Override
    public void revokeTokensByUserIdAndType(Long userId, AuthToken.TokenType tokenType) {
        try {
            List<AuthToken> tokens = findByUserIdAndType(userId, tokenType);
            for (AuthToken token : tokens) {
                token.revoke();
                save(token);
            }
            log.debug("撤销用户指定类型令牌成功: userId={}, tokenType={}", userId, tokenType);
        } catch (Exception e) {
            log.error("撤销用户指定类型令牌失败: userId={}, tokenType={}", userId, tokenType, e);
        }
    }

    @Override
    public void updateTokenStatus(String tokenId, AuthToken.TokenStatus status) {
        try {
            Optional<AuthToken> tokenOpt = findByTokenId(tokenId);
            if (tokenOpt.isPresent()) {
                AuthToken token = tokenOpt.get();
                token.setStatus(status);
                save(token);
                log.debug("更新令牌状态成功: tokenId={}, status={}", tokenId, status);
            }
        } catch (Exception e) {
            log.error("更新令牌状态失败: tokenId={}, status={}", tokenId, status, e);
        }
    }

    @Override
    public void refreshTokenExpireTime(String tokenId, long expireMinutes) {
        try {
            Optional<AuthToken> tokenOpt = findByTokenId(tokenId);
            if (tokenOpt.isPresent()) {
                AuthToken token = tokenOpt.get();
                token.refreshExpireTime(expireMinutes);
                
                String tokenKey = getTokenKey(tokenId);
                redisService.setCacheObject(tokenKey, token, expireMinutes, TimeUnit.MINUTES);
                
                log.debug("刷新令牌过期时间成功: tokenId={}, expireMinutes={}", tokenId, expireMinutes);
            }
        } catch (Exception e) {
            log.error("刷新令牌过期时间失败: tokenId={}, expireMinutes={}", tokenId, expireMinutes, e);
        }
    }

    @Override
    public void cleanExpiredTokens() {
        try {
            // Redis的过期机制会自动清理过期的令牌
            // 这里可以添加额外的清理逻辑，比如清理用户令牌集合中的过期令牌ID
            log.debug("清理过期令牌完成");
        } catch (Exception e) {
            log.error("清理过期令牌失败", e);
        }
    }

    @Override
    public boolean existsByTokenId(String tokenId) {
        try {
            String tokenKey = getTokenKey(tokenId);
            return redisService.hasKey(tokenKey);
        } catch (Exception e) {
            log.error("检查令牌是否存在失败: tokenId={}", tokenId, e);
            return false;
        }
    }

    @Override
    public long countActiveTokensByUserId(Long userId) {
        try {
            return findActiveTokensByUserId(userId).size();
        } catch (Exception e) {
            log.error("统计用户活跃令牌数量失败: userId={}", userId, e);
            return 0;
        }
    }

    @Override
    public Optional<AuthToken> findLatestAccessTokenByUserId(Long userId) {
        try {
            return findByUserIdAndType(userId, AuthToken.TokenType.ACCESS_TOKEN)
                    .stream()
                    .filter(token -> AuthToken.TokenStatus.ACTIVE.equals(token.getStatus()))
                    .max((t1, t2) -> t1.getLoginTime().compareTo(t2.getLoginTime()));
        } catch (Exception e) {
            log.error("查询用户最新访问令牌失败: userId={}", userId, e);
            return Optional.empty();
        }
    }

    private String getTokenKey(String tokenId) {
        return TOKEN_KEY_PREFIX + tokenId;
    }

    private String getUserTokenKey(Long userId) {
        return USER_TOKEN_KEY_PREFIX + userId;
    }
}
